/*
 * Copyright (c) 2025 CAX Conversion Project
 */
package com.comac.ins.isfd.core.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.anwen.mongo.conditions.interfaces.Projection;
import com.anwen.mongo.conditions.query.QueryChainWrapper;
import com.anwen.mongo.conditions.query.QueryWrapper;
import com.anwen.mongo.mapper.BaseMapper;
import com.anwen.mongo.mapping.TypeReference;
import com.comac.ins.common.core.utils.MessageUtils;
import com.comac.ins.common.core.utils.StringUtils;
import com.comac.ins.isfd.constant.MongoDataConstant;
import com.comac.ins.isfd.core.IMaterialGetService;
import com.comac.ins.isfd.domain.IsfdMetadata;
import com.comac.ins.isfd.domain.vo.MaterialCountObjectVo;
import com.mongodb.client.AggregateIterable;
import com.mongodb.client.MongoCollection;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zb
 * @version v1.0
 * @className IMaterialGetFromMongoService
 * @description 从mongo获取材料数据实现类
 * @date 2024/6/14 10:45
 **/
@Service("mongodb")
@Slf4j
public class MaterialGetFromMongoService implements IMaterialGetService, MongoDataConstant {

    @Autowired
    private BaseMapper baseMapper;

    /**
     * 第三层开始来源其他地方，例如半结构化的来源于mongoDb
     *
     * @param isfdMetadata
     * @return List<Map < String, Object>>
     */
    @Override
    public List<Map<String, Object>> generateTreeNode(IsfdMetadata isfdMetadata, Boolean detail) {
        //
        if (MAT_MATERIAL.equals(isfdMetadata.getTableName())) {
            return generateTreeNode4Mat(isfdMetadata, detail);
        } else if (COMP_MATERIAL.equals(isfdMetadata.getTableName())) {
            return generateTreeNode4Comp(isfdMetadata, detail);
        } else {
            log.error("generateTreeNode error table name, isfdMetadata: " + isfdMetadata);
        }
        return null;
    }

    /**
     * 通过材料类型来统计材料数据
     *
     * @param isfdMetadata
     * @return List<MaterialCountObject>
     */
    @Override
    public List<MaterialCountObjectVo> countByType(IsfdMetadata isfdMetadata) {
        List<MaterialCountObjectVo> countList = new ArrayList<>();
        if (MAT_MATERIAL.equals(isfdMetadata.getTableName())) {
            MongoCollection mongoCollection = baseMapper.getMongoPlusClient().getCollection(DB_NAME, isfdMetadata.getTableName());
            List<Document> pipeline = Arrays.asList(
                    new Document($_GROUP, new Document(ID, $ + BASIC_TYPE).append(COUNT, new Document($_SUM, INCLUDE)))
            );
            AggregateIterable<Document> aggregateIterable = mongoCollection.aggregate(pipeline);
            aggregateIterable.forEach(document -> {
                MaterialCountObjectVo materialCountObjectVo = new MaterialCountObjectVo();
                materialCountObjectVo.setMaterialType(document.getString(ID));
                materialCountObjectVo.setCount(document.getLong(COUNT));
                materialCountObjectVo.setMaterialName(MessageUtils.message(ISFD_PREFIX + materialCountObjectVo.getMaterialType()));
                countList.add(materialCountObjectVo);
            });
        } else if (COMP_MATERIAL.equals(isfdMetadata.getTableName())) {
            MaterialCountObjectVo materialCountObjectVo = new MaterialCountObjectVo();
            materialCountObjectVo.setMaterialType(COMP_MATERIAL);
            materialCountObjectVo.setCount(baseMapper.count(DB_NAME, isfdMetadata.getTableName()));
            materialCountObjectVo.setMaterialName(MessageUtils.message(ISFD_PREFIX + materialCountObjectVo.getMaterialType()));
            countList.add(materialCountObjectVo);
        } else {
            log.error("countByType error table name, isfdMetadata: " + isfdMetadata);
        }
        return countList;
    }

    /**
     * 复合材料直接返回原始数据，p_index排序
     *
     * @param isfdMetadata
     * @param detail
     * @return
     */
    private List<Map<String, Object>> generateTreeNode4Comp(IsfdMetadata isfdMetadata, Boolean detail) {
        QueryChainWrapper wrapper = new QueryWrapper();
        wrapper.orderByAsc(P_INDEX);
        List<Map<String, Object>> materials = baseMapper.list(isfdMetadata.getTableName(), new TypeReference<Map<String, Object>>() {
        });
        serizlizedId(materials);
        return materials;
    }

    /**
     * 金属材料树形结构通过basic.type来分组，最后一级是series
     *
     * @param isfdMetadata
     * @param detail
     * @return
     */
    private List<Map<String, Object>> generateTreeNode4Mat(IsfdMetadata isfdMetadata, Boolean detail) {
        QueryChainWrapper wrapper = new QueryWrapper();
//        if (null == detail || !detail.booleanValue()) {
//            wrapper = buildQueryProjectWrapper();
//        }
        wrapper.orderByAsc(BASIC_TYPE).orderByAsc(BASIC_SERIES);
        List<Map<String, Object>> materials = baseMapper.list(isfdMetadata.getTableName(), wrapper, new TypeReference<Map<String, Object>>() {
        });
        Map<String, List<Map<String, Object>>> materialsMap = groupByBasicType((materials));
        List<Map<String, Object>> result = generateTreeNodeByBasicType(materialsMap, detail);
        return result;
    }

    @NotNull
    private List<Map<String, Object>> generateTreeNodeByBasicType(Map<String, List<Map<String, Object>>> materialsMap, Boolean detail) {
        List<Map<String, Object>> result = new ArrayList<>();
        materialsMap.forEach((key, list) -> {
            Map<String, Object> node = new HashMap<>();
            node.put(TYPE, key);
            node.put(SERIES, list.stream().map(m -> {
                String series = null;
                if (m.containsKey(BASIC)) {
                    series = ((Document) m.get(BASIC)).getString(SERIES);
                }
                return series;
                //用LinkedHashSet保持排序
            }).collect(Collectors.toCollection(LinkedHashSet::new)));
            result.add(node);
        });
        return result;
    }

    /**
     * 按照basic。type分组
     *
     * @param materials
     * @return
     */
    private Map<String, List<Map<String, Object>>> groupByBasicType(List<Map<String, Object>> materials) {
        Map<String, List<Map<String, Object>>> materialsMap = materials.stream().
                collect(Collectors.groupingBy(m -> ((Document) m.get(BASIC)).getString(TYPE),
                        //用LinkedHashMap保持排序
                        LinkedHashMap::new, Collectors.toList()));
        return materialsMap;
    }

    /**
     * 树形结构数据只需要返回部分字段即可，金属材料树形最后一级是series
     *
     * @return QueryChainWrapper
     */
    @NotNull
    private QueryChainWrapper buildQueryProjectWrapper() {
        QueryChainWrapper wrapper = new QueryWrapper();
        wrapper.project(
                new Projection(ID, EXCLUDE.intValue()),
                new Projection(BASIC_TYPE, INCLUDE.intValue()),
                new Projection(BASIC_SERIES, INCLUDE.intValue()));
        return wrapper;
    }

    /**
     * @param isfdMetadata
     * @param params
     * @return
     */
    @Override
    public List<Map<String, Object>> getTreeNodeDetail(IsfdMetadata isfdMetadata, String[] params) {
        List<Map<String, Object>> result = baseMapper.getByColumn(DB_NAME, isfdMetadata.getTableName(), BASIC_SERIES, params[0], new TypeReference<Map<String, Object>>() {
        });
        serizlizedIdAndName(result);
        return result;
    }

    /**
     * @param isfdMetadata
     * @param queryParams
     * @return
     */
    @Override
    public List<Map<String, Object>> getNodeDataByParams(IsfdMetadata isfdMetadata, Map<String, Object> queryParams) {
        QueryChainWrapper queryChainWrapper = new QueryWrapper();
        if (CollectionUtil.isNotEmpty(queryParams)) {
            queryParams.forEach((k, v) -> {
                queryChainWrapper.eq(StringUtils.isNotBlank(v.toString()), k, v);
            });
        }
        List<Map<String, Object>> result = baseMapper.list(isfdMetadata.getTableName(), queryChainWrapper, new TypeReference<Map<String, Object>>() {
        });
        return result;
    }

    /**
     * 通过basic.series查询basic.basis,basic.specification,basic.temper,basic.thickness,basic.form，并去重，每个字段返回数组，
     * 满足字典列表接口
     *
     * @param isfdMetadata
     * @param series
     * @return
     */
    public Map<String, Object> getBasicBySeriesGroupByBasic(IsfdMetadata isfdMetadata, String series) {
        MongoCollection mongoCollection = baseMapper.getMongoPlusClient().getCollection(DB_NAME, isfdMetadata.getTableName());
        List<Document> pipeline = Arrays.asList(
                //过滤
                new Document($_MATCH, new Document(BASIC_SERIES, series)),
                //分组
                new Document($_GROUP, new Document(ID, null).append(BASIS, new Document($_ADD_TO_SET, $ + BASIC_BASIS))
                        .append(SPECIFICATION, new Document($_ADD_TO_SET, $ + BASIC_SPECIFICATION))
                        .append(TEMPER, new Document($_ADD_TO_SET, $ + BASIC_TEMPER))
                        .append(THICKNESS, new Document($_ADD_TO_SET, $ + BASIC_THICKNESS))
                        .append(FORM, new Document($_ADD_TO_SET, $ + BASIC_FORM))),
                //返回映射
                new Document($_PROJECT, new Document(ID, EXCLUDE).append(BASIS, INCLUDE)
                        .append(SPECIFICATION, INCLUDE)
                        .append(TEMPER, INCLUDE)
                        .append(THICKNESS, INCLUDE)
                        .append(FORM, INCLUDE))
        );
        Map<String, Object> result = new HashMap<>();
        AggregateIterable<Document> aggregateIterable = mongoCollection.aggregate(pipeline);
        Document document = aggregateIterable.first();
        if (null != document && !document.isEmpty()) {
            result.put(SERIES, series);
            result.put(BASIS, converList(document, BASIS));
            result.put(SPECIFICATION, converList(document, SPECIFICATION));
            result.put(TEMPER, converList(document, TEMPER));
            result.put(THICKNESS, converList(document, THICKNESS));
            result.put(FORM, converList(document, FORM));
        }
        return result;
    }

    /**
     * 把返回的数据中的数组转成字符串，用逗号分割
     *
     * @param document
     * @return
     */
    private List<String> converList(Document document, String key) {
        List list = (List) document.get(key);
        if (CollectionUtil.isNotEmpty(list)) {
            List<String> result = (List) list.stream().map(obj -> {
                if (obj instanceof List) {
                    return String.join(",", (List) obj);
                } else {
                    return obj.toString();
                }
            }).sorted().collect(Collectors.toList());
            return result;
        }
        return new ArrayList<>();
    }

    /***
     * @description
     * @param materials mongodb返回的_id是ObjectID类型，需要序列化处理成string
     * @return
     */
    private void serizlizedId(List<Map<String, Object>> materials) {
        if (CollectionUtil.isEmpty(materials)) {
            return;
        }
        //处理_id问题
        materials.stream().map(m -> {
            return serizlizedId(m);
        }).collect(Collectors.toList());
    }

    /***
     * @description
     * @param m mongodb返回的_id是ObjectID类型，需要序列化处理成string
     * @return Map<String, Object>
     */
    private Map<String, Object> serizlizedId(Map<String, Object> m) {
        if (CollectionUtil.isNotEmpty(m) && m.containsKey(ID)) {
            m.put(ID, m.get(ID).toString());
        }
        return m;
    }

    /***
     * @description
     * @param materials mongodb返回的_id是ObjectID类型，需要序列化处理成string
     *                  结构项目name需要用6个basic的字段拼接起来
     * @return
     */
    private void serizlizedIdAndName(List<Map<String, Object>> materials) {
        if (CollectionUtil.isEmpty(materials)) {
            return;
        }
        //处理_id问题
        materials.stream().map(m -> {
            return serizlizedIdAndName(m);
        }).collect(Collectors.toList());
    }

    /***
     * @description
     * @param m mongodb返回的_id是ObjectID类型，需要序列化处理成string
     *                  结构项目name需要用6个basic的字段拼接起来
     * @return
     */
    private Map<String, Object> serizlizedIdAndName(Map<String, Object> m) {
        if (CollectionUtil.isNotEmpty(m) && m.containsKey(ID)) {
            m.put(ID, m.get(ID).toString());
            if (m.containsKey(BASIC)) {
                Document basic = (Document) m.get(BASIC);
                String name = String.join("_", getStringValue(basic, SERIES),
                        getStringValue(basic, SPECIFICATION), getStringValue(basic, FORM),
                        getStringValue(basic, TEMPER), getStringValue(basic, THICKNESS),
                        getStringValue(basic, BASIS));
                m.put(NAME, name);
            }
        }
        return m;
    }

    /**
     * basic 里面的数据可能是list，list数据处理成string
     *
     * @param document
     * @param key
     * @return
     */
    private String getStringValue(Document document, String key) {
        String value = "";
        if (document.get(key) instanceof List) {
            value = String.join(",", document.getList(key, String.class));
        } else {
            value = document.getString(key);
        }
        return value;
    }
}
